/*-------------------------------------------------------------------------
 *
 * bloom.h
 *	  Bloom 索引的头文件。
 *
 * Copyright (c) 2016-2022, PostgreSQL Global Development Group
 *
 * IDENTIFICATION
 *	  contrib/bloom/bloom.h
 *
 *-------------------------------------------------------------------------
 */
#ifndef _BLOOM_H_
#define _BLOOM_H_

#include "access/amapi.h"
#include "access/generic_xlog.h"
#include "access/itup.h"
#include "access/xlog.h"
#include "fmgr.h"
#include "nodes/pathnodes.h"

/* 支持程序编号 */
#define BLOOM_HASH_PROC			1
#define BLOOM_OPTIONS_PROC		2
#define BLOOM_NPROC				2

/* 扫描策略 */
#define BLOOM_EQUAL_STRATEGY	1
#define BLOOM_NSTRATEGIES		1

/* Bloom 页的不透明部分 */
typedef struct BloomPageOpaqueData
{
	OffsetNumber maxoff;		/* 页上的索引元组数量 */
	uint16		flags;			/* 见下面的位定义 */
	uint16		unused;			/* 占位符以强制最大对齐 BloomPageOpaqueData 的大小，并将 bloom_page_id 精确放置在页面末尾 */
	uint16		bloom_page_id;	/* 用于识别 BLOOM 索引 */
} BloomPageOpaqueData;

typedef BloomPageOpaqueData *BloomPageOpaque;

/* Bloom 页面标志 */
#define BLOOM_META		(1<<0)
#define BLOOM_DELETED	(2<<0)

/*
 * 页面 ID 是为了方便 pg_filedump 和类似工具，它们将很难区分不同索引类型的页面。
 * 它应该是页面上的最后 2 个字节。由于对齐考虑，这在某种程度上是“免费的”。
 *
 * 见上面的 GinPageOpaqueData 注释。
 */
#define BLOOM_PAGE_ID		0xFF83

/* 访问 Bloom 页面结构的宏 */
#define BloomPageGetOpaque(page) ((BloomPageOpaque) PageGetSpecialPointer(page))
#define BloomPageGetMaxOffset(page) (BloomPageGetOpaque(page)->maxoff)
#define BloomPageIsMeta(page) \
	((BloomPageGetOpaque(page)->flags & BLOOM_META) != 0)
#define BloomPageIsDeleted(page) \
	((BloomPageGetOpaque(page)->flags & BLOOM_DELETED) != 0)
#define BloomPageSetDeleted(page) \
	(BloomPageGetOpaque(page)->flags |= BLOOM_DELETED)
#define BloomPageSetNonDeleted(page) \
	(BloomPageGetOpaque(page)->flags &= ~BLOOM_DELETED)
#define BloomPageGetData(page)		((BloomTuple *)PageGetContents(page))
#define BloomPageGetTuple(state, page, offset) \
	((BloomTuple *)(PageGetContents(page) \
		+ (state)->sizeOfBloomTuple * ((offset) - 1)))
#define BloomPageGetNextTuple(state, tuple) \
	((BloomTuple *)((Pointer)(tuple) + (state)->sizeOfBloomTuple))

/* 保留页面编号 */
#define BLOOM_METAPAGE_BLKNO	(0)
#define BLOOM_HEAD_BLKNO		(1) /* 第一数据页面 */

/*
 * 我们将 Bloom 签名存储为 uint16 字数组。
 */
typedef uint16 BloomSignatureWord;

#define SIGNWORDBITS ((int) (BITS_PER_BYTE * sizeof(BloomSignatureWord)))

/*
 * 默认和最大 Bloom 签名长度（以位为单位）。
 */
#define DEFAULT_BLOOM_LENGTH	(5 * SIGNWORDBITS)
#define MAX_BLOOM_LENGTH		(256 * SIGNWORDBITS)

/*
 * 每个索引键生成的默认和最大签名位数。
 */
#define DEFAULT_BLOOM_BITS		2
#define MAX_BLOOM_BITS			(MAX_BLOOM_LENGTH - 1)

/* Bloom 索引选项 */
typedef struct BloomOptions
{
	int32		vl_len_;		/* varlena 头部（请勿直接触摸！） */
	int			bloomLength;	/* 签名长度（以字为单位，非位！） */
	int			bitSize[INDEX_MAX_KEYS];	/* 为每个索引键生成的位数 */
} BloomOptions;

/*
 * FreeBlockNumberArray - 块编号数组，大小使元数据填充 metapage 中的所有空间。
 */
typedef BlockNumber FreeBlockNumberArray[
										 MAXALIGN_DOWN(
													   BLCKSZ - SizeOfPageHeaderData - MAXALIGN(sizeof(BloomPageOpaqueData))
													   - MAXALIGN(sizeof(uint16) * 2 + sizeof(uint32) + sizeof(BloomOptions))
													   ) / sizeof(BlockNumber)
];

/* Bloom 索引的元数据 */
typedef struct BloomMetaPageData
{
	uint32		magickNumber;
	uint16		nStart;
	uint16		nEnd;
	BloomOptions opts;
	FreeBlockNumberArray notFullPage;
} BloomMetaPageData;

/* 用于区分 Bloom 页的魔法数字 */
#define BLOOM_MAGICK_NUMBER (0xDBAC0DED)

/* 适合 BloomMetaPageData 的块编号数量 */
#define BloomMetaBlockN		(sizeof(FreeBlockNumberArray) / sizeof(BlockNumber))

#define BloomPageGetMeta(page)	((BloomMetaPageData *) PageGetContents(page))

typedef struct BloomState
{
	FmgrInfo	hashFn[INDEX_MAX_KEYS];
	Oid			collations[INDEX_MAX_KEYS];
	BloomOptions opts;			/* 索引元页面上的选项副本 */
	int32		nColumns;

	/*
	 * sizeOfBloomTuple 是特定于索引的，并且取决于 reloptions，因此
	 * 预先计算它
	 */
	Size		sizeOfBloomTuple;
} BloomState;

#define BloomPageGetFreeSpace(state, page) \
	(BLCKSZ - MAXALIGN(SizeOfPageHeaderData) \
		- BloomPageGetMaxOffset(page) * (state)->sizeOfBloomTuple \
		- MAXALIGN(sizeof(BloomPageOpaqueData)))

/*
 * 元组与所有其他关系非常不同
 */
typedef struct BloomTuple
{
	ItemPointerData heapPtr;
	BloomSignatureWord sign[FLEXIBLE_ARRAY_MEMBER];
} BloomTuple;

#define BLOOMTUPLEHDRSZ offsetof(BloomTuple, sign)

/* Bloom 索引扫描的不透明数据结构 */
typedef struct BloomScanOpaqueData
{
	BloomSignatureWord *sign;	/* 扫描签名 */
	BloomState	state;
} BloomScanOpaqueData;

typedef BloomScanOpaqueData *BloomScanOpaque;

/* blutils.c */
extern void _PG_init(void);
extern void initBloomState(BloomState *state, Relation index);
extern void BloomFillMetapage(Relation index, Page metaPage);
extern void BloomInitMetapage(Relation index);
extern void BloomInitPage(Page page, uint16 flags);
extern Buffer BloomNewBuffer(Relation index);
extern void signValue(BloomState *state, BloomSignatureWord *sign, Datum value, int attno);
extern BloomTuple *BloomFormTuple(BloomState *state, ItemPointer iptr, Datum *values, bool *isnull);
extern bool BloomPageAddItem(BloomState *state, Page page, BloomTuple *tuple);

/* blvalidate.c */
extern bool blvalidate(Oid opclassoid);

/* 索引访问方法接口函数 */
extern bool blinsert(Relation index, Datum *values, bool *isnull,
					 ItemPointer ht_ctid, Relation heapRel,
					 IndexUniqueCheck checkUnique,
					 bool indexUnchanged,
					 struct IndexInfo *indexInfo);
extern IndexScanDesc blbeginscan(Relation r, int nkeys, int norderbys);
extern int64 blgetbitmap(IndexScanDesc scan, TIDBitmap *tbm);
extern void blrescan(IndexScanDesc scan, ScanKey scankey, int nscankeys,
					 ScanKey orderbys, int norderbys);
extern void blendscan(IndexScanDesc scan);
extern IndexBuildResult *blbuild(Relation heap, Relation index,
								 struct IndexInfo *indexInfo);
extern void blbuildempty(Relation index);
extern IndexBulkDeleteResult *blbulkdelete(IndexVacuumInfo *info,
										   IndexBulkDeleteResult *stats, IndexBulkDeleteCallback callback,
										   void *callback_state);
extern IndexBulkDeleteResult *blvacuumcleanup(IndexVacuumInfo *info,
											  IndexBulkDeleteResult *stats);
extern bytea *bloptions(Datum reloptions, bool validate);
extern void blcostestimate(PlannerInfo *root, IndexPath *path,
						   double loop_count, Cost *indexStartupCost,
						   Cost *indexTotalCost, Selectivity *indexSelectivity,
						   double *indexCorrelation, double *indexPages);

#endif
